社区分享 | 拯救 TensorFlow Lite 编译痛点
本文来自社区投稿与征集,作者:胡旭华,Google Developers Expert。
本文转自:https://blog.csdn.net/sun_28
互联网上有很多关于 TensorFlow Lite 的 Android JNI 编译方法,但关于 TensorFlow Lite 本身的编译却不太好找。因此,我把自己的相关经验做一个总结分享。分享前先感谢一下小雨同学和谷歌 TensorFlow Lite 团队的朋友,他们为我提供了宝贵的经验和建议。
官网方法
TensorFlow 官网在 2.0 版本发布后做出相应的更新,TensorFlow Lite (简称 TFLite) 的资料在 For Mobile & IOT 版块,Android 的官方推荐方法在相应的 Guide 里面。
For Mobile & IOT
https://tensorflow.google.cn/liteGuide
https://tensorflow.google.cn/lite/guide/android
执行 ./configure 脚本,设置下面几个环境变量参数:
ANDROID_SDK_HOME
ANDROID_SDK_API_LEVEL
ANDROID_NDK_HOME
ANDROID_NDK_API_LEVEL
这些 configure 的环境变量,也可以在 TensorFlow 主目录下的 .tf_configure.bazelrc
文件,参考下面的配置方式,设定环境参数。
build --action_env ANDROID_NDK_HOME="/usr/local/android/android-ndk-r17c"
build --action_env ANDROID_NDK_API_LEVEL="18"
build --action_env ANDROID_BUILD_TOOLS_VERSION="28.0.3"
build --action_env ANDROID_SDK_API_LEVEL="23"
build --action_env ANDROID_SDK_HOME="/usr/local/android/android-sdk-linux"
只要 Bazel 配置完成,我们就可以在 TensorFlow 主目录用下面的脚本编译 TensorFlow Lite AAR。
bazel build --cxxopt='-std=c++11' -c opt \
--fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a \
//tensorflow/lite/java:tensorflow-lite
最终的编译结果libtensorflowlite_jni.so
都会在bazel-genfiles/tensorflow/lite/java/
相应的 CPU 架构目录中。
如果直接调用 TFLite 的 JNI 接口能满足 Android 应用的业务需求,到这里已经解决问题了。
其他方法
我的业务场景要求利用 TFLite 的 C 类接口封装推理引擎,支撑业务架构的算法实现。因此,libtensorflowlite_jni.so
的可见函数不足以解决我面临的问题。
下面是 TFLite_jni 的符号导出情况:
...
000076e5 g DF .text0000000c VERS_1.0 Java_org_tensorflow_lite_nnapi_NnApiDelegate_createDelegate
00007b19 g DF .text0000003c VERS_1.0 Java_org_tensorflow_lite_NativeInterpreterWrapper_getInputCount
0000ce8d g DF .text00000068 VERS_1.0 Java_org_tensorflow_lite_Tensor_buffer
00007cb5 g DF .text00000030 VERS_1.0 Java_org_tensorflow_lite_NativeInterpreterWrapper_allowFp16PrecisionForFp32
0000823d g DF .text00000078 VERS_1.0 Java_org_tensorflow_lite_NativeInterpreterWrapper_getOutputQuantizationZeroPoint
00008351 g DF .text000002c0 VERS_1.0 Java_org_tensorflow_lite_NativeInterpreterWrapper_resizeInput
0000d935 g DF .text00000050 VERS_1.0 Java_org_tensorflow_lite_TensorFlowLite_schemaVersion
00007d41 g DF .text00000034 VERS_1.0
.....
解决思路有两种,一种是修改tensorflow/lite/java下的 exported_symbols.lds文件:
exported_symbols.lds
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/java/src/main/native/exported_symbols.lds
*Java_*
*JNI_OnLoad
*JNI_OnUnload
TfLite*
*tflite*
*TFL_*
另一种思路是,直接编译 tensorflow/lite
下的libtensorflowlite.so
:
bazel build --cxxopt='-std=c++11' -c opt \
--config=android_arm \
//tensorflow/lite:libtensorflowlite.so
--config=android_arm
表示兼容 armeabi-v7a 架构的 Android 交叉编译,详情可以参考 tensorflow 主目录下的 tensorflow/BUILD
文件:....
config_setting(
name = "android_arm",
values = {
"crosstool_top": "//external:android/crosstool",
"cpu": "armeabi-v7a",
},
visibility = ["//visibility:public"],
)
....
最后,展示部分 TFLite 比较完整的导出符号:
.....
00cd155 w DF .text000000a0 VERS_1.0 _ZN6tflite3ops7builtin15maximum_minimum4EvalILNS2_10KernelTypeE0ENS2_9MinimumOpEEE12TfLiteStatusP13TfLiteContextP10TfLiteNode
0013ea65 g DF .text0000000c VERS_1.0 _ZN6tflite8internal7MfccDctC1Ev
0004f7ed w DF .text00000648 VERS_1.0 _ZN6tflite3ops7builtin11activations14LogSoftmaxEvalILNS2_10KernelTypeE1EEE12TfLiteStatusP13TfLiteContextP10TfLiteNode
000642bd w DF .text0000029c VERS_1.0 _ZN6tflite13reference_ops9ArgMinMaxIhixNSt6__ndk18functionIFbhhEEEEEvRKNS_12RuntimeShapeEPKT_PKT1_S8_PT0_RKT2_
00098673 w DF .text00000254 VERS_1.0 _ZN6tflite13optimized_ops24FloatDepthwiseConvKernelILb1ELi0ELi1EE3RunEiiiPKfiS4_Pf
000d101d g DF .text00000006 VERS_1.0 _ZN6tflite3ops7builtin3mul4InitEP13TfLiteContextPKcj
001323b1 g DF .text00000eb0 VERS_1.0 _ZN6tflite3ops7builtin28unidirectional_sequence_lstm26CheckInputTensorDimensionsEP13TfLiteContextP10TfLiteNodeiiib
0019bb28 w DO .data.rel.ro00000014 VERS_1.0 _ZTVN6tflite16cpu_backend_gemm6detail14CustomGemvTaskIffffLNS0_18QuantizationFlavorE0EEE
000f9c93 w DF .text00000008 VERS_1.0 _ZZN6tflite3ops7builtin6reduce8EvalTypeIhEE12TfLiteStatusP13TfLiteContextP10TfLiteNodePNS2_9OpContextENS2_10ReduceTypeEENUlhhE0_8__invokeEhh
000a990d w DF .text00000998 VERS_1.0 _ZN6tflite3ops7builtin3div7EvalDivILNS2_10KernelTypeE2EEEvP13TfLiteContextP10TfLiteNodeP15TfLiteDivParamsPKNS2_6OpDataEPK12TfLiteTensorSG_PSE_
0011fa0d w DF .text00000b02 VERS_1.0 _ZN6tflite3ops7builtin3sub13EvalQuantizedILNS2_10KernelTypeE0EEEvP13TfLiteContextP10TfLiteNodeP15TfLiteSubParamsPKNS2_6OpDataEPK12TfLiteTensorSG_PSE_
00048bc9 g DF .text00000028 VERS_1.0 _ZN6tflite3ops7builtin11activations11SoftmaxInitEP13TfLiteContextPKcj
00094afd w DF .text000002a4 VERS_1.0 _ZN6tflite3ops7builtin14depthwise_conv23EvalQuantizedPerChannelILNS2_10KernelTypeE0EEEvP13TfLiteContextP10TfLiteNodeP25TfLiteDepthwiseConvParamsPNS2_6OpDataEPK12TfLiteTensorSF_SF_PSD_
00108941 w DF .text0000015c VERS_1.0 _ZN6tflite13reference_ops13RankOneSelectIbaEEvRKNS_12RuntimeShapeEPKT_S4_PKT0_S4_SA_S4_PS8_
00104965 w DF .text00000462 VERS_1.0 _ZN6tflite13reference_ops15ReverseSequenceIsiEEvPKT0_iiRKNS_12RuntimeShapeEPKT_S7_PS8_
0010b70d w DF .text0000019c VERS_1.0 _ZN6tflite3ops7builtin5slice22GetBeginAndSizeVectorsIxEEviPK12TfLiteTensorS6_PNSt6__ndk16vectorIiNS7_9allocatorIiEEEESC_
000887b5 w DF .text00000004 VERS_1.0 _ZN6tflite16cpu_backend_gemm6detail14CustomGemvTaskIffffLNS0_18QuantizationFlavorE0EED0Ev
0011d101 w DF .text000005b8 VERS_1.0 _ZN6tflite13reference_ops12StridedSliceIxEEvRKNS_18StridedSliceParamsERKNS_12RuntimeShapeEPKT_S7_PS8_
000516db w DF .text00000006 VERS_1.0 _ZNSt6__ndk110__function6__funcIZN6tflite3ops7builtin11activations11TanhPrepareILNS5_10KernelTypeE0EEE12TfLiteStatusP13TfLiteContextP10TfLiteNodeEUlfE0_NS_9allocatorISD_EEFffEEclEOf
......
有了这个符号完整的 TFLite 动态库,我们就可以封装自己的 JNI 推理引擎了。
了解更多:https://github.com/SunAriesCN。